﻿using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Publishing;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace VariationFixUpCsom
{
    class Program
    {
        static DataTable dtRelation;
        static string[] skipFixList = null;
        private static string[] fixOnlyList;
        private static bool fixVariation;

        static void Main(string[] args)
        {
            fixVariation = bool.Parse(ConfigurationManager.AppSettings["Update"]);
            skipFixList = System.IO.File.ReadAllLines("Skip.txt");
            skipFixList = skipFixList.Select(s => s.ToLowerInvariant()).ToArray();

            fixOnlyList = System.IO.File.ReadAllLines("Fix.txt");
            fixOnlyList = fixOnlyList.Select(s => s.ToLowerInvariant()).ToArray();

            System.IO.File.WriteAllText("Log.txt", "");
            System.IO.File.WriteAllText("Report.csv", "Page Url, Source Group ID, Target Group Id, Source Relationship Id,Target Relationship Id, Fixed" + System.Environment.NewLine);
            SecureString pwd = new SecureString();

            Log("Started the tool with Update: "+ fixVariation +", with Mode: " + ConfigurationManager.AppSettings["Mode"]);

            foreach (var c in ConfigurationManager.AppSettings["Password"])
            {
                pwd.AppendChar(c);
            }
            using (ClientContext ctx = new ClientContext(ConfigurationManager.AppSettings["Site"] + "/" + ConfigurationManager.AppSettings["Source"]))
            {
                ctx.Credentials = new SharePointOnlineCredentials(ConfigurationManager.AppSettings["User"], pwd);

                Web sourceWeb = ctx.Site.OpenWeb(ConfigurationManager.AppSettings["Source"]);
                Web targetWeb = ctx.Site.OpenWeb(ConfigurationManager.AppSettings["Target"]);
                List relationshipList = ctx.Site.RootWeb.Lists.GetByTitle("Relationships List");

                ctx.Load(targetWeb);
                ctx.Load(sourceWeb);
                ctx.Load(relationshipList, l => l.RootFolder, l => l.RootFolder.ServerRelativeUrl);
                ctx.ExecuteQuery();
                //PopulateVariationRelation(ctx, relationshipList);
                //if (!fixVariation)
                //{
                //    PopulateVariationRelation(ctx, relationshipList);
                //}

                List sourcePagesList = sourceWeb.Lists.GetByTitle("Pages");

                ListItemCollectionPosition licp = null;
                DateTime startDate = DateTime.Now;
                Log("Started at: " + startDate);

                do
                {
                    CamlQuery query = new CamlQuery();
                    string queryText = "<View Scope='RecursiveAll'>'";
                    queryText += "<ViewFields>";
                    queryText += "<FieldRef Name='EntOldDocumentName' />";
                    queryText += "<FieldRef Name='PublishingVariationGroupID' />";
                    queryText += "<FieldRef Name='PublishingVariationRelationshipLinkFieldID' />";
                    queryText += "<FieldRef Name='FileRef' />";
                    queryText += "</ViewFields>";
                    queryText += "<RowLimit>4999</RowLimit></View>";

                    query.ViewXml = queryText;

                    query.ListItemCollectionPosition = licp;

                    var items = sourcePagesList.GetItems(query);
                    ctx.Load(items, itms => itms.ListItemCollectionPosition, i => i.Include(item => item.Id, item => item.File, item => item["EntOldDocumentName"], item => item["FileRef"], item => item.Id
                        , item => item["PublishingVariationGroupID"], 
                        item => item["PublishingVariationRelationshipLinkFieldID"], 
                        item => item.File, item=>item.FileSystemObjectType));
                    ctx.ExecuteQuery();
                    licp = items.ListItemCollectionPosition;

                    foreach (var item in items)
                    {
                        if (item.FileSystemObjectType == FileSystemObjectType.Folder)
                            continue;

                        string sourceUrl = item["FileRef"].ToString();

                        try
                        {
                            //string targetUrl = sourceUrl.Replace("/" + ConfigurationManager.AppSettings["Source"] + "/", "/" + ConfigurationManager.AppSettings["Target"] + "/");
                            var targetUrlResult = Variations.GetPeerUrl(ctx, sourceUrl, ConfigurationManager.AppSettings["Target"]);

                            try
                            {
                                ctx.ExecuteQuery();

                                if (!string.IsNullOrEmpty(targetUrlResult.Value) && !targetUrlResult.Value.ToLowerInvariant().TrimEnd('/').Equals(targetWeb.ServerRelativeUrl.ToLowerInvariant().TrimEnd('/')))
                                {
                                    Log(sourceUrl + " has variation " + targetUrlResult.Value);
                                    continue;
                                }
                            }
                            catch
                            {

                            }

                            Log("Found a breakage at: " + sourceUrl);

                            string targetUrl = sourceUrl.Replace("/" + ConfigurationManager.AppSettings["Source"] + "/", "/" + ConfigurationManager.AppSettings["Target"] + "/");

                            File targetPage = targetWeb.GetFileByServerRelativeUrl(targetUrl);
                            ListItem targetPageItem = targetPage.ListItemAllFields;
                            ctx.Load(targetPage);
                            ctx.Load(targetPageItem);
                            ctx.ExecuteQuery();

                            Guid sourceGuid = item["PublishingVariationGroupID"] != null
                                && !string.IsNullOrEmpty(item["PublishingVariationGroupID"].ToString())
                                ? new Guid(item["PublishingVariationGroupID"].ToString()) : Guid.Empty;

                            Guid targetGuid = targetPageItem["PublishingVariationGroupID"] != null
                                && !string.IsNullOrEmpty(targetPageItem["PublishingVariationGroupID"].ToString())
                                ? new Guid(targetPageItem["PublishingVariationGroupID"].ToString()) : Guid.Empty;

                            int targetRelationshipLinkId = getRelationId(targetPageItem["PublishingVariationRelationshipLinkFieldID"]);
                            int sourceRelationshipLinkId = getRelationId(item["PublishingVariationRelationshipLinkFieldID"]);

                            if (sourceGuid == Guid.Empty || targetGuid == Guid.Empty || sourceGuid != targetGuid)
                            {
                                // Log("Found a breakage at: " + sourceUrl);

                                if (canFixSource(sourceUrl))
                                {
                                    bool fixSource = false;
                                    bool fixTarget = false;
                                    Guid commonGuid = Guid.Empty;

                                    if (sourceGuid != Guid.Empty)
                                    {
                                        fixTarget = true;
                                        commonGuid = sourceGuid;
                                    }
                                    else if (targetGuid != Guid.Empty)
                                    {
                                        fixSource = true;
                                        commonGuid = targetGuid;
                                    }
                                    else
                                    {
                                        fixSource = true;
                                        fixTarget = true;
                                        commonGuid = Guid.NewGuid();
                                    }

                                    if (fixSource)
                                    {
                                        FixItem(ctx, item, commonGuid, sourceWeb.ServerRelativeUrl, item.File, relationshipList);


                                    }

                                    if (fixTarget)
                                    {
                                        FixItem(ctx, targetPageItem, commonGuid, targetWeb.ServerRelativeUrl, targetPage, relationshipList);
                                    }


                                    System.IO.File.AppendAllText("Report.csv",
                                        string.Format(
                                        "{0}, {1}, {2}, {5} ,{3}, {4}",
                                        sourceUrl, sourceGuid, targetGuid, targetRelationshipLinkId, true, sourceRelationshipLinkId)
                                        + System.Environment.NewLine);

                                }
                                else
                                {
                                    Log("Skipping " + item["FileRef"].ToString() + " as it is not flagged to be fixed.");
                                    System.IO.File.AppendAllText("Report.csv",
                                        string.Format(
                                        "{0}, {1}, {2}, {5} ,{3}, {4}",
                                        sourceUrl, sourceGuid, targetGuid, targetRelationshipLinkId, false, sourceRelationshipLinkId)
                                        + System.Environment.NewLine);
                                }

                            }
                            else
                            {
                                Log("Unable to fix " + item["FileRef"].ToString() + " as guids are proper and matiching ");
                                System.IO.File.AppendAllText("Report.csv",
                                                string.Format(
                                                "{0}, {1}, {2}, {5} ,{3}, {4},\"{6}\"",
                                                sourceUrl, sourceGuid, targetGuid, targetRelationshipLinkId, false, sourceRelationshipLinkId, "guids are proper and matiching")
                                                + System.Environment.NewLine);
                            }

                        }
                        catch (Exception ex)
                        {
                            Log("Unable to process " + item["FileRef"].ToString() + " due to error: " + ex.Message);
                            System.IO.File.AppendAllText("Report.csv",
                                            string.Format(
                                            "{0}, {1}, {2}, {5} ,{3}, {4},\"{6}\"",
                                            sourceUrl, Guid.Empty, Guid.Empty, 0, false, 0, ex.Message)
                                            + System.Environment.NewLine);

                        }

                    }
                } while (licp != null);


            }
        }

        private static bool canFixSource(string sourceUrl)
        {
            bool canFix = false;
            string mode = ConfigurationManager.AppSettings["Mode"];

            if (fixVariation)
            {
                if (mode.Equals("FixOnly", StringComparison.InvariantCultureIgnoreCase))
                {
                    canFix = fixOnlyList.Contains(sourceUrl.ToLowerInvariant());
                }
                else if (mode.Equals("SkipOnly", StringComparison.InvariantCultureIgnoreCase))
                {
                    canFix = !skipFixList.Contains(sourceUrl.ToLowerInvariant());
                }
            }

            return canFix;
        }

        private static void PopulateVariationRelation(ClientContext ctx, List relationshipList)
        {
            //var lists = ctx.Site.RootWeb.Lists;
            //ctx.Load(lists);
            //ctx.ExecuteQuery();

            //foreach (var lst in lists)
            //{
            //    Log(lst.Title);
            //}

            //var itemDel = relationshipList.GetItemById(10795);
            //itemDel.DeleteObject();
            //ctx.ExecuteQuery();
            //itemDel = relationshipList.GetItemById(10796);
            //itemDel.DeleteObject();

            //ctx.ExecuteQuery();
            //////itemDel = relationshipList.GetItemById(10794);
            //////itemDel.DeleteObject();
            //////ctx.ExecuteQuery();
            //return;

            System.IO.File.WriteAllText("VariationList.csv", "ID, GroupGuid, EntryType,ObjectID,ParentAreaID,Deleted,status" + System.Environment.NewLine);

            dtRelation = new DataTable();
            dtRelation.Columns.Add("ID");
            dtRelation.Columns.Add("GroupGuid");
            dtRelation.Columns.Add("EntryType");
            dtRelation.Columns.Add("ObjectID");
            dtRelation.Columns.Add("ParentAreaID");
            dtRelation.Columns.Add("Deleted");
            dtRelation.Columns.Add("Status");

            ListItemCollectionPosition licp = null;

            do
            {
                Log("Retrieving Relations...");
                CamlQuery query = new CamlQuery();
                string queryText = "<View Scope='RecursiveAll'>'";
                queryText += "<ViewFields>";
                queryText += "<FieldRef Name='ID' />";
                queryText += "<FieldRef Name='GroupGuid' />";
                queryText += "<FieldRef Name='EntryType' />";
                queryText += "<FieldRef Name='ObjectID' />";
                queryText += "<FieldRef Name='ParentAreaID' />";
                queryText += "<FieldRef Name='Deleted' />";
                queryText += "<FieldRef Name='Status' />";
                queryText += "</ViewFields>";
                queryText += "<RowLimit>4999</RowLimit></View>";

                query.ViewXml = queryText;

                query.ListItemCollectionPosition = licp;

                var items = relationshipList.GetItems(query);
                ctx.Load(items, itms => itms.ListItemCollectionPosition, i => i.Include(item => item.Id,
                    item => item["ID"], item => item["GroupGuid"], item => item.Id
                    , item => item["EntryType"], item => item["ObjectID"]
                    , item => item["ParentAreaID"], item => item["Deleted"], item => item["Status"]));
                ctx.ExecuteQuery();
                licp = items.ListItemCollectionPosition;

                foreach (var item in items)
                {
                    DataRow dr = dtRelation.NewRow();

                    dr["ID"] = item.Id;
                    dr["GroupGuid"] = item["GroupGuid"] != null ? item["GroupGuid"].ToString() : "";
                    dr["EntryType"] = item["EntryType"] != null ? item["EntryType"].ToString() : "";
                    dr["ObjectID"] = item["ObjectID"] != null ? ((FieldUrlValue)item["ObjectID"]).Url : "";
                    dr["ParentAreaID"] = item["ParentAreaID"] != null ? ((FieldUrlValue)item["ParentAreaID"]).Url : "";
                    dr["Deleted"] = item["Deleted"] != null ? item["Deleted"].ToString() : "";
                    dr["Status"] = item["Status"] != null ? item["Status"].ToString() : "";

                    dtRelation.Rows.Add(dr);

                    System.IO.File.AppendAllText("VariationList.csv", string.Format("{0},{1},{2},{3},{4},{5},{6}",
                        item.Id, dr["GroupGuid"].ToString(), dr["EntryType"].ToString(),
                        dr["ObjectID"].ToString(), dr["ParentAreaID"].ToString(), dr["Deleted"].ToString(),
                        dr["Status"].ToString()) + System.Environment.NewLine);
                }

            } while (licp != null);
        }



        private static void FixItem(ClientContext ctx, ListItem item, Guid commonGuid, string siteUrl, File file, List relationshipList)
        {
            file.CheckOut();
            ctx.ExecuteQuery();



            ListItemCreationInformation info = new ListItemCreationInformation();
            info.FolderUrl = relationshipList.RootFolder.ServerRelativeUrl;
            info.UnderlyingObjectType = FileSystemObjectType.File;

            var newItem = relationshipList.AddItem(info);
            newItem["GroupGuid"] = commonGuid;
            newItem["EntryType"] = 0;
            newItem["ObjectID"] = item["FileRef"].ToString();
            newItem["ParentAreaID"] = siteUrl;
            newItem["Deleted"] = false;
            newItem["Status"] = 3;
            newItem.Update();
            ctx.Load(newItem, i => i.Id, i => i["FileLeafRef"], i => i["FileRef"]);
            ctx.ExecuteQuery();

            string newUrl = ConfigurationManager.AppSettings["Site"] + "/" + "Relationships%20List/" + newItem["FileLeafRef"];
            string desc = newItem["FileRef"].ToString().Replace(" ", "%20");

            FieldUrlValue urlValue = new FieldUrlValue();
            urlValue.Description = desc;
            urlValue.Url = newUrl;

            item["PublishingVariationGroupID"] = commonGuid.ToString();
            item["PublishingVariationRelationshipLinkFieldID"] = urlValue;

            item.Update();
            file.CheckIn("", CheckinType.MajorCheckIn);
            file.Publish("");
            file.Approve("");
            ctx.ExecuteQuery();
        }

        private static int getRelationId(object relationId)
        {
            int id = 0;

            if (relationId != null)
            {
                string url = ((FieldUrlValue)relationId).Url;
                id = int.Parse(url.Substring(url.LastIndexOf("/") + 1).Replace("_.000", ""));
            }

            return id;
        }

        //public static void Process(Site site, string pageName, ClientContext ctx)
        //{

        //    Web rootWeb = site.RootWeb;

        //    Log(string.Format("Begin processing site collection '{0}'.", site.ServerRelativeUrl));

        //    List relationshipList = rootWeb.Lists.GetByTitle("Relationships List");
        //    List variationLabelsList = rootWeb.Lists.GetByTitle("Variation Labels");
        //    PublishingWeb sourceLabelWeb = null;
        //    Dictionary<PublishingWeb, bool> labelWebs = new Dictionary<PublishingWeb, bool>();
        //    ListItemCollection varitionLabelItems = variationLabelsList.GetItems(CamlQuery.CreateAllItemsQuery());
        //    ctx.Load(varitionLabelItems);
        //    ctx.ExecuteQuery();

        //    List<ListItem> variationItems = new List<ListItem>();

        //    foreach (ListItem item in varitionLabelItems)
        //    {
        //        Log(string.Format("Getting variation web '{0}'.", item["Label"]));

        //        Web web = site.OpenWeb(item["Label"].ToString());

        //        if ((bool)item["Is Source"])
        //            sourceLabelWeb = PublishingWeb.GetPublishingWeb(ctx, web);

        //        labelWebs.Add(PublishingWeb.GetPublishingWeb(ctx,web), (bool)item["Is Source"]);
        //    }
        //    if (sourceLabelWeb == null)
        //        throw new Exception("Unable to identify source label web.");

        //    Dictionary<PublishingPage, Guid> sourcePages = new Dictionary<PublishingPage, Guid>();

        //    Log(string.Format("Begin resetting GroupIDs."));

        //    // First - make sure that all the matching pages have the same group ID
        //    foreach (PublishingPage page in sourceLabelWeb.GetPublishingPages())
        //    {
        //        if (!(pageName == null || pageName.ToLowerInvariant() == page.Name.ToLowerInvariant()))
        //            continue;

        //        Log(string.Format("Procesing page '{0}'.", page.Url));

        //        Guid groupID = Guid.Empty;
        //        if (page.Fields.ContainsField("PublishingVariationGroupID"))
        //        {
        //            if (page.ListItem["PublishingVariationGroupID"] + "" != "" &&
        //                page.ListItem["PublishingVariationGroupID"] + "" != Guid.Empty.ToString())
        //            {
        //                groupID = new Guid(page.ListItem["PublishingVariationGroupID"] + "");
        //                Log(
        //                    string.Format("GroupID '{0}' found in variation source '{1}'.", groupID,
        //                                  sourceLabelWeb.Url));
        //            }
        //        }
        //        else
        //        {
        //            Log(string.Format("Unable to locate PublishingVariationGroupID field for page '{0}'", pageName));
        //        }
        //        if (groupID == Guid.Empty)
        //        {
        //            Log(string.Format("GroupID not found in source - begin searching variations."));
        //            // See if we can find a group ID in matching pages within the other variations
        //            foreach (PublishingWeb varWeb in labelWebs.Keys)
        //            {
        //                if (labelWebs[varWeb])
        //                    continue; // Don't consider the source as we've already done that
        //                try
        //                {
        //                    // Get the matching page
        //                    PublishingPage varPage = varWeb.GetPublishingPages()[page.Url];
        //                    if (varPage == null)
        //                        continue;
        //                    if (varPage.Fields.ContainsField("PublishingVariationGroupID"))
        //                    {
        //                        // If the matching page has a group ID then use that
        //                        if (varPage.ListItem["PublishingVariationGroupID"] + "" != "")
        //                            groupID = new Guid(varPage.ListItem["PublishingVariationGroupID"] + "");
        //                    }
        //                }
        //                catch (ArgumentException)
        //                {
        //                }
        //                if (groupID.ToString() != Guid.Empty.ToString())
        //                {
        //                    Log(string.Format("GroupID '{0}' found in variation '{1}'.", groupID, varWeb.Url));
        //                    break;
        //                }
        //            }
        //        }
        //        if (groupID == Guid.Empty)
        //        {
        //            groupID = Guid.NewGuid();
        //            Log(string.Format("GroupID not found - new GroupID created: '{0}'.", groupID));
        //        }

        //        // Now that we have a groupID reset all pages to use that same groupID
        //        Log(string.Format("Begin resetting variations to use new GroupID."));
        //        foreach (PublishingWeb varWeb in labelWebs.Keys)
        //        {
        //            try
        //            {
        //                // Get the matching page
        //                PublishingPage varPage = varWeb.GetPublishingPages()[page.Url];
        //                if (varPage == null)
        //                    continue;
        //                if (varPage.Fields.ContainsField("PublishingVariationGroupID"))
        //                {
        //                    // Set the groupID if it doesn't match what we have
        //                    if ((varPage.ListItem["PublishingVariationGroupID"] + "").ToLower() !=
        //                        groupID.ToString().ToLower())
        //                    {
        //                        Log(
        //                            string.Format("Assigning GroupID to page '{0}' in variation '{1}'.",
        //                                          varPage.Url, varWeb.Url));

        //                        varPage.ListItem["PublishingVariationGroupID"] = groupID.ToString();
        //                        varPage.ListItem.SystemUpdate();
        //                    }
        //                }
        //            }
        //            catch (ArgumentException)
        //            {
        //            }
        //        }
        //        Log(string.Format("Finished resetting variations to use new GroupID."));

        //        sourcePages.Add(page, groupID);
        //    }
        //    // Now that all the pages have been reset to use the same group ID for each variation we can now deal with the relationships list
        //    Log(string.Format("Finished resetting GroupIDs.\r\n"));
        //    Log(string.Format("Begin processing of Relationships List."));

        //    foreach (PublishingPage page in sourcePages.Keys)
        //    {
        //        foreach (PublishingWeb varWeb in labelWebs.Keys)
        //        {
        //            Log(string.Format("Processing page '{0}' on variation '{1}'", page.Url, varWeb.Url));

        //            SPFieldUrlValue objectID = new SPFieldUrlValue();
        //            objectID.Description = varWeb.Web.ServerRelativeUrl + "/" + page.Url;
        //            objectID.Url = site.MakeFullUrl(objectID.Description);
        //            string groupID = sourcePages[page].ToString();

        //            SPListItem relationshipItem = null;
        //            foreach (SPListItem item in relationshipList.Items)
        //            {
        //                if (item["ObjectID"].ToString().ToLower() == objectID.ToString().ToLower())
        //                {
        //                    Log(
        //                        string.Format("Found item in relationships list matching ObjectID '{0}'.",
        //                                      objectID.Description));

        //                    relationshipItem = item;
        //                    relationshipItem["Deleted"] = varWeb.GetPublishingPages()[page.Url] == null;
        //                    break;
        //                }
        //            }
        //            if (relationshipItem == null)
        //            {
        //                Log(
        //                    string.Format(
        //                        "Unable to locate item in Relationships List for ObjectID '{0}' - creating a new item.",
        //                        objectID.Description));
        //                // We couldn't find a matching item for the variation so we have to create one
        //                relationshipItem =
        //                    relationshipList.Items.Add(relationshipList.RootFolder.ServerRelativeUrl,
        //                                               SPFileSystemObjectType.File);
        //                relationshipItem["ObjectID"] = objectID.ToString();
        //                relationshipItem["Deleted"] = varWeb.GetPublishingPages()[page.Url] == null;
        //            }
        //            relationshipItem["GroupID"] = groupID;
        //            relationshipItem.Update();

        //            Log(
        //                string.Format("Relationships List item updated - assigning link to page '{0}'.",
        //                              page.Url));

        //            // Now that the relationship list item is set the way we need it we have to link the corresponding page to the item.
        //            try
        //            {
        //                PublishingPage varPage = varWeb.GetPublishingPages()[page.Url];
        //                if (varPage == null)
        //                    continue;
        //                string newLinkID = "/" +
        //                                   (rootWeb.ServerRelativeUrl + "/" + relationshipItem.Url).Trim('/');
        //                newLinkID = site.MakeFullUrl(newLinkID.Replace(" ", "%20")) + ", " + newLinkID;

        //                varPage.ListItem["PublishingVariationRelationshipLinkFieldID"] = newLinkID;
        //                varPage.ListItem.SystemUpdate();
        //            }
        //            catch (ArgumentException)
        //            {
        //            }
        //        }
        //    }
        //    Log(string.Format("Finished processing of Relationships List."));

        //    foreach (PublishingWeb web in labelWebs.Keys)
        //    {
        //        web.Web.Dispose();
        //    }
        //    Log(string.Format("Finished processing site collection '{0}'.", site.ServerRelativeUrl));

        //}

        private static void Log(string p)
        {
            Console.WriteLine(p);
            System.IO.File.AppendAllText("Log.txt", p + System.Environment.NewLine);
        }

    }
}
